package ru.inse.core.jsonrpc.spring;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.web.HttpRequestHandler;

import ru.inse.core.jsonrpc.AnnotationsErrorResolver;
import ru.inse.core.jsonrpc.ErrorResolver;
import ru.inse.core.jsonrpc.JsonRpcServer;


public class JsonService
    implements HttpRequestHandler, InitializingBean, ApplicationContextAware
{
    private ObjectMapper objectMapper;
    private JsonRpcServer jsonRpcServer;
    private ApplicationContext applicationContext;
    private ErrorResolver errorResolver = null;
    private boolean backwardsComaptible = true;
    private boolean rethrowExceptions = false;
    private boolean allowExtraParams = false;
    private boolean allowLessParams = false;
    private Class<?> serviceInterface;
    private Object serviceImpl;

    public void afterPropertiesSet()
        throws Exception
    {
        // find the ObjectMapper
        if (objectMapper == null && applicationContext != null
            && applicationContext.containsBean("objectMapper"))
        {
            objectMapper = (ObjectMapper)applicationContext.getBean("objectMapper");
        }
        if (objectMapper == null && applicationContext != null)
        {
            try
            {
                objectMapper = (ObjectMapper)BeanFactoryUtils.beanOfTypeIncludingAncestors(
                    applicationContext, ObjectMapper.class);
            }
            catch (Exception e)
            {
                objectMapper = new ObjectMapper();
                objectMapper.getJsonFactory().configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false);
            }
        }
        // make sure we have an error resolver
        if (errorResolver == null)
        {
            errorResolver = new AnnotationsErrorResolver();
        }
        // create the server
        jsonRpcServer = new JsonRpcServer(objectMapper, getServiceImpl(), getServiceInterface(),
            errorResolver);
        jsonRpcServer.setBackwardsComaptible(backwardsComaptible);
        jsonRpcServer.setRethrowExceptions(rethrowExceptions);
        jsonRpcServer.setAllowExtraParams(allowExtraParams);
        jsonRpcServer.setAllowLessParams(allowLessParams);
    }

    public void handleRequest(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        jsonRpcServer.handle(request, response);
        response.getOutputStream().flush();
    }

    public void setApplicationContext(ApplicationContext applicationContext)
    {
        this.applicationContext = applicationContext;
    }

    public void setObjectMapper(ObjectMapper objectMapper)
    {
        this.objectMapper = objectMapper;
    }

    public void setErrorResolver(ErrorResolver errorResolver)
    {
        this.errorResolver = errorResolver;
    }

    public void setBackwardsComaptible(boolean backwardsComaptible)
    {
        this.backwardsComaptible = backwardsComaptible;
    }

    public void setRethrowExceptions(boolean rethrowExceptions)
    {
        this.rethrowExceptions = rethrowExceptions;
    }

    public void setAllowExtraParams(boolean allowExtraParams)
    {
        this.allowExtraParams = allowExtraParams;
    }

    public void setAllowLessParams(boolean allowLessParams)
    {
        this.allowLessParams = allowLessParams;
    }

    public Class<?> getServiceInterface()
    {
        return serviceInterface;
    }

    public void setServiceInterface(Class<?> serviceInterface)
    {
        this.serviceInterface = serviceInterface;
    }

    public Object getServiceImpl()
    {
        return serviceImpl;
    }

    public void setServiceImpl(Object serviceImpl)
    {
        this.serviceImpl = serviceImpl;
    }
}
